package by.bucha.tabnavigationproject.tabsframework;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import by.bucha.tabnavigationproject.R;

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Window;

public abstract class AbstractTabActivity extends SherlockFragmentActivity {

	/**
	 * Set of all fragments that were added once to this activity in order to
	 * distinguish between add and attach.
	 */
	protected List<Fragment> mFragmentsWasAdded = new ArrayList<Fragment>();

	/**
	 * Maps the tab index to the tab info object.
	 */
	@SuppressLint("UseSparseArrays")
	private final Map<Integer, TabInfo> mTabMap = new HashMap<Integer, TabInfo>();

	/**
	 * Increment.Used for creating tab's tags.
	 */
	private AtomicInteger mIdCounter = new AtomicInteger();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.d(TAG, "onCreate , AbstractTabActivity");
		if (savedInstanceState == null) {
			// This has to be called before setContentView and you must use the
			// class in com.actionbarsherlock.view and NOT android.view
			requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
		}

		getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
		setContentView(R.layout.tab_navigation);

		// Prevent display of progress bar at startup in android 2.2...
		setSupportProgressBarIndeterminateVisibility(false);

		ActionBar actionBar = getSupportActionBar();
		actionBar.setDisplayHomeAsUpEnabled(false);
		actionBar.setDisplayShowHomeEnabled(false);
		actionBar.setDisplayShowTitleEnabled(false);
		if (savedInstanceState == null) {
			createTabs();
		}
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		saveDataBeforeRotate(outState);
		super.onSaveInstanceState(outState);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);
		restoreDataAfterRotate(savedInstanceState);
	}

	public static final String EXTRA_TABS_COUNT = "how much tabs created";
	public static final String EXTRA_TAB_TEXT = "tab's text";
	public static final String EXTRA_TAB_TAG = "tab's tag";
	public static final String EXTRA_CURRENTLY_SELECTED_TAB = "currently selected tab";
	public static final String EXTRA_TAB_FRAGMENTS_COUNT = "count of fragments which opened in tab";
	public static final String EXTRA_FRAGMENT = "fragment to save";
	public static final String EXTRA_FRAGMENTS_WAS_ADDED = "it is fragment which was added to mFragmentsWasAdded";
	public static final String EXTRA_FRAGMENTS_WAS_ADDED_COUNT = "it is mFragmentsWasAdded.Count()";
	public static final String TAB_NUMBER_TAG = "_It is tab № ";
	public static final String FRAGMENT_TEXT_NUMBER = "_fragment № ";

	public static final String TAG = "AbstractTabActivity";

	/**
	 * Saving all data into outState.
	 * 
	 * @param outState
	 */
	private void saveDataBeforeRotate(Bundle outState) {
		int tabCount = getSupportActionBar().getNavigationItemCount();
		Log.d(TAG, "saveDataBeforeRotate");

		outState.putInt(EXTRA_TABS_COUNT, tabCount);

		int currentlySelectedTabIndex = getSupportActionBar()
				.getSelectedNavigationIndex();
		outState.putInt(EXTRA_CURRENTLY_SELECTED_TAB, currentlySelectedTabIndex);

		for (int tabNumber = 0; tabNumber < tabCount; tabNumber++) {
			String tabNumberTag = TAB_NUMBER_TAG + tabNumber;
			String tabText = (String) getSupportActionBar().getTabAt(tabNumber)
					.getText();
			outState.putString(EXTRA_TAB_TEXT + tabNumberTag, tabText);

			TabInfo tabInfo = mTabMap.get(tabNumber);
			Integer tabTag = tabInfo.mTag;
			outState.putInt(EXTRA_TAB_TAG + tabNumberTag, tabTag);

			int fragmentsCountInTab = tabInfo.mTabFragments.size();
			outState.putInt(EXTRA_TAB_FRAGMENTS_COUNT + tabNumberTag,
					fragmentsCountInTab);

			// saving all fragments which currently opened in tab
			for (int fragmentInTabNumber = 0; fragmentInTabNumber < fragmentsCountInTab; fragmentInTabNumber++) {
				String fragmentTextNumber = tabNumberTag + FRAGMENT_TEXT_NUMBER
						+ fragmentInTabNumber;
				Fragment fragment = tabInfo.mTabFragments
						.get(fragmentInTabNumber);

				int fragmentsWasAddedSize = mFragmentsWasAdded.size();
				for (int increment = 0; increment < fragmentsWasAddedSize; increment++) {
					if (mFragmentsWasAdded.get(increment).getClass()
							.equals(fragment.getClass())) {
						// TODO: переписано
						getSupportFragmentManager().putFragment(outState,
								EXTRA_FRAGMENT + fragmentTextNumber, fragment);
						break;
					}
				}
			}
		}

		// saving set of fragments which was added to FragmentTransaction
		int addedFragmentsCount = mFragmentsWasAdded.size();
		outState.putInt(EXTRA_FRAGMENTS_WAS_ADDED_COUNT, addedFragmentsCount);

		for (int fragmentNumber = 0; fragmentNumber < addedFragmentsCount; fragmentNumber++) {
			String fragmentTextNumber = FRAGMENT_TEXT_NUMBER + fragmentNumber;
			Fragment fragment = mFragmentsWasAdded.get(fragmentNumber);
			getSupportFragmentManager().putFragment(outState,
					EXTRA_FRAGMENTS_WAS_ADDED + fragmentTextNumber, fragment);
		}
		Log.d(TAG, "saveDataBeforeRotate ------SAVED");
	}

	/**
	 * Restore data after rotate.
	 * 
	 * @param savedInstanceState
	 */
	private void restoreDataAfterRotate(Bundle savedInstanceState) {

		// restoring set of fragments which was added to FragmentTransaction
		int addedFragmentsCount = savedInstanceState
				.getInt(EXTRA_FRAGMENTS_WAS_ADDED_COUNT);
		Log.d(TAG, "restoreDataAfterRotate");

		for (int fragmentNumber = 0; fragmentNumber < addedFragmentsCount; fragmentNumber++) {
			String fragmentTextNumber = FRAGMENT_TEXT_NUMBER + fragmentNumber;

			Fragment fragment = getSupportFragmentManager().getFragment(
					savedInstanceState,
					EXTRA_FRAGMENTS_WAS_ADDED + fragmentTextNumber);
			if (!mFragmentsWasAdded.contains(fragment)) {
				mFragmentsWasAdded.add(fragment);
			}
		}

		int tabCount = savedInstanceState.getInt(EXTRA_TABS_COUNT);

		for (int tabNumber = 0; tabNumber < tabCount; tabNumber++) {
			String tabNumberTag = TAB_NUMBER_TAG + tabNumber;

			String tabText = savedInstanceState.getString(EXTRA_TAB_TEXT
					+ tabNumberTag);

			TabInfo tabInfo = new TabInfo(this);
			Integer tabTag = savedInstanceState.getInt(EXTRA_TAB_TAG
					+ tabNumberTag);
			tabInfo.mTag = tabTag;
			Fragment startFragment = null;
			startFragment = getTabStartFragment(tabTag);
			if (startFragment != null) {
				tabInfo.mTabFragments.push(startFragment);
			}

			int fragmentsCountInTab = savedInstanceState
					.getInt(EXTRA_TAB_FRAGMENTS_COUNT + tabNumberTag);

			// restoring all fragments which was opened in tab
			for (int fragmentInTabNumber = 0; fragmentInTabNumber < fragmentsCountInTab; fragmentInTabNumber++) {
				String fragmentTextNumber = tabNumberTag + FRAGMENT_TEXT_NUMBER
						+ fragmentInTabNumber;
				Fragment fragment = getSupportFragmentManager()
						.getFragment(savedInstanceState,
								EXTRA_FRAGMENT + fragmentTextNumber);
				if (fragment != null) {
					tabInfo.mTabFragments.push(fragment);
				}
			}
			createTab(tabInfo, tabText);
		}

		int currentlySelectedTabIndex = savedInstanceState
				.getInt(EXTRA_CURRENTLY_SELECTED_TAB);
		getSupportActionBar().setSelectedNavigationItem(
				currentlySelectedTabIndex);
		Log.d(TAG, "restoreDataAfterRotate-RESTORED");
	}

	/**
	 * Create tab while restore data.
	 * 
	 * @param tabInfo
	 * @param tabText
	 */
	private void createTab(TabInfo tabInfo, String tabText) {
		Log.d(TAG, "createTab " + tabText);
		ActionBar.Tab tab = getSupportActionBar().newTab();
		tab.setText(tabText);
		tab.setTag(tabInfo.mTag);
		tab.setTabListener(new TabListenerFromTabsFramework(tabInfo,
				getSupportFragmentManager()));
		getSupportActionBar().addTab(tab);
		mTabMap.put(tabInfo.mTag, tabInfo);
	}

	/**
	 * Create tag for tab.
	 * 
	 * @return {@link Integer}
	 */
	private Integer createId() {
		return mIdCounter.getAndIncrement();
	}

	/**
	 * Add a tab and the tab content. Call it in createTabs.
	 * 
	 * @param tab
	 * @param fragment
	 */
	public void addTab(ActionBar.Tab tab, Fragment fragment) {
		Log.d(TAG, "Add tab " + tab.getText());
		Integer tag = createId();
		TabInfo tabInfo = new TabInfo(this);
		tabInfo.mTabFragments.push(fragment);
		tabInfo.mTag = tag;
		tab.setTag(tag);

		tab.setTabListener(new TabListenerFromTabsFramework(tabInfo,
				getSupportFragmentManager()));
		getSupportActionBar().addTab(tab);
		mTabMap.put(tag, tabInfo);

	}

	/**
	 * Returns fragment if it wasn't into mFragmentsWasAdded, or null.
	 * 
	 * @param tabTag
	 * @return {@link Fragment}
	 */
	private Fragment getTabStartFragment(Integer tabTag) {
		List<Fragment> startFragments = getStartFragments();
		Fragment fragment = startFragments.get(tabTag);
		int fragmentsWasAddedSize = mFragmentsWasAdded.size();
		for (int increment = 0; increment < fragmentsWasAddedSize; increment++) {
			if (mFragmentsWasAdded.get(increment).getClass()
					.equals(fragment.getClass())) {
				return null;
			}
		}
		return fragment;
	}

	@Override
	public void onBackPressed() {
		TabInfo tabInfo = getCurrentTabInfo();
		if (tabInfo.mTabFragments.size() > 1) {
			popFragment(tabInfo);
		} else {
			super.onBackPressed();
		}
	}

	/**
	 * Callback method (part of onCreate) for adding all tabs with addTab
	 */
	public abstract void createTabs();

	/**
	 * Returns list of start fragment for each tab.
	 * 
	 * @return {@link List}
	 */
	public abstract List<Fragment> getStartFragments();

	/**
	 * Gets TabInfo of current tab.
	 * 
	 * @return {@link TabInfo}
	 */
	public TabInfo getCurrentTabInfo() {
		int index = getSupportActionBar().getSelectedNavigationIndex();
		TabInfo tabInfo = mTabMap.get(index);
		return tabInfo;
	}

	/**
	 * Pop the current fragment from the tabs back stack and show the previous
	 * fragment.
	 * 
	 * @param tabInfo
	 *            - current TabInfo
	 */
	public void popFragment(TabInfo tabInfo) {
		final FragmentManager fragmentManager = getSupportFragmentManager();
		final FragmentTransaction ft = fragmentManager.beginTransaction();
		tabInfo.detachFragment(ft, tabInfo.mTabFragments.pop());
		tabInfo.attachFragment(ft, tabInfo.mTabFragments.peek());
		ft.commit();
	}

	/**
	 * Push fragment into tab stack.
	 * 
	 */

	public void pushFragment(Fragment fragment) {
		TabInfo tabInfo = getCurrentTabInfo();
		FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
		tabInfo.detachFragment(ft, tabInfo.mTabFragments.peek());
		tabInfo.attachFragment(ft, fragment);
		tabInfo.mTabFragments.push(fragment);
		ft.commit();
	}

	/**
	 * Refresh fragment view,just attach detach fragment.
	 * 
	 */
	public void refreshFragmentView(Fragment fragment) {
		TabInfo tabInfo = getCurrentTabInfo();
		if (tabInfo.mTabFragments.peek() == fragment) {
			FragmentTransaction ft = getSupportFragmentManager()
					.beginTransaction();
			tabInfo.detachFragment(ft, fragment);
			tabInfo.attachFragment(ft, fragment);
			ft.commit();
		}
	}
}
